Telegram Group & Telegram Channel
🖥 Задача для собеседования: "Быстрая очередь с удалением элемента за O(1)"

🔖 Условие:

Реализуйте структуру данных — очередь (`queue`), поддерживающую три операции:

- enqueue(x) — добавить элемент в конец очереди (должно работать за O(1));
- dequeue() — удалить элемент из начала очереди и вернуть его (должно работать за O(1));
- remove(x) — удалить первое вхождение элемента x из очереди за O(1).

Ограничения:
- Элементы могут повторяться.
- Если элемент отсутствует, remove(x) не делает ничего.
- Операции должны оставаться эффективными на больших объемах данных (миллионы элементов).
- Можно использовать стандартные структуры данных STL (`list`, unordered_map, unordered_set и т.д.).

▪️ Дополнительные вопросы к кандидату:

- Как обеспечить O(1) удаление произвольного элемента из очереди?
- Как избежать утечек памяти или "битых" итераторов после удаления элементов?
- Что будет, если очередь будет содержать множество одинаковых элементов?
- Как бы вы изменили решение, если бы нужно было удалять все вхождения элемента, а не только первое?

---

▪️ Разбор возможного решения:


Основная архитектура:

- Используем std::list<int> data для хранения реальной очереди.
- Почему list? Потому что позволяет за O(1) удалять элементы по итератору.
- Используем std::unordered_map<int, std::list<std::list<int>::iterator>> index.
- Для каждого значения храним список итераторов на его вхождения в data.

Операции:

- enqueue(x):
- Добавляем x в конец data.
- Сохраняем итератор на него в index[x].
- Все действия — за O(1).

- dequeue():
- Удаляем элемент из начала data.
- Находим соответствующий итератор в index, удаляем его.
- Если в списке итераторов для этого значения больше нет элементов, удаляем ключ из index.

- remove(x):
- Если в index[x] есть итераторы:
- Берем первый итератор.
- Удаляем его из data и из списка итераторов.
- Если список стал пустым — удаляем запись x из index.
- Все действия — за O(1).

---

▪️ Возможные подводные камни:

- Не проверять, пустой ли index[x], перед удалением итератора.
- Не удалять ключи из unordered_map, что приводит к накоплению "пустых" списков в памяти.
- Использовать vector вместо list — тогда удаление из середины будет занимать O(n).

---

▪️ Мини-пример интерфейса класса:


#include <list>
#include <unordered_map>

class FastQueue {
private:
std::list<int> data;
std::unordered_map<int, std::list<std::list<int>::iterator>> index;

public:
void enqueue(int x) {
auto it = data.insert(data.end(), x);
index[x].push_back(it);
}

int dequeue() {
if (data.empty()) throw std::out_of_range("Queue is empty");
int val = data.front();
auto it = index[val].front();
index[val].pop_front();
if (index[val].empty()) {
index.erase(val);
}
data.pop_front();
return val;
}

void remove(int x) {
if (index.count(x) == 0) return;
auto it = index[x].front();
data.erase(it);
index[x].pop_front();
if (index[x].empty()) {
index.erase(x);
}
}
};


▪️ Дополнительные вопросы на усложнение:


- Что если нужно сделать removeAll(x) — удаление всех вхождений элемента?
- Как изменится решение, если в очереди будут сложные объекты вместо int?
- Как минимизировать использование памяти, если очередь очень большая?
- Как обеспечить безопасность потоков (thread-safety) для многопоточного варианта очереди?

@cpluspluc
Please open Telegram to view this post
VIEW IN TELEGRAM



tg-me.com/cpluspluc/1047
Create:
Last Update:

🖥 Задача для собеседования: "Быстрая очередь с удалением элемента за O(1)"

🔖 Условие:

Реализуйте структуру данных — очередь (`queue`), поддерживающую три операции:

- enqueue(x) — добавить элемент в конец очереди (должно работать за O(1));
- dequeue() — удалить элемент из начала очереди и вернуть его (должно работать за O(1));
- remove(x) — удалить первое вхождение элемента x из очереди за O(1).

Ограничения:
- Элементы могут повторяться.
- Если элемент отсутствует, remove(x) не делает ничего.
- Операции должны оставаться эффективными на больших объемах данных (миллионы элементов).
- Можно использовать стандартные структуры данных STL (`list`, unordered_map, unordered_set и т.д.).

▪️ Дополнительные вопросы к кандидату:

- Как обеспечить O(1) удаление произвольного элемента из очереди?
- Как избежать утечек памяти или "битых" итераторов после удаления элементов?
- Что будет, если очередь будет содержать множество одинаковых элементов?
- Как бы вы изменили решение, если бы нужно было удалять все вхождения элемента, а не только первое?

---

▪️ Разбор возможного решения:


Основная архитектура:

- Используем std::list<int> data для хранения реальной очереди.
- Почему list? Потому что позволяет за O(1) удалять элементы по итератору.
- Используем std::unordered_map<int, std::list<std::list<int>::iterator>> index.
- Для каждого значения храним список итераторов на его вхождения в data.

Операции:

- enqueue(x):
- Добавляем x в конец data.
- Сохраняем итератор на него в index[x].
- Все действия — за O(1).

- dequeue():
- Удаляем элемент из начала data.
- Находим соответствующий итератор в index, удаляем его.
- Если в списке итераторов для этого значения больше нет элементов, удаляем ключ из index.

- remove(x):
- Если в index[x] есть итераторы:
- Берем первый итератор.
- Удаляем его из data и из списка итераторов.
- Если список стал пустым — удаляем запись x из index.
- Все действия — за O(1).

---

▪️ Возможные подводные камни:

- Не проверять, пустой ли index[x], перед удалением итератора.
- Не удалять ключи из unordered_map, что приводит к накоплению "пустых" списков в памяти.
- Использовать vector вместо list — тогда удаление из середины будет занимать O(n).

---

▪️ Мини-пример интерфейса класса:


#include <list>
#include <unordered_map>

class FastQueue {
private:
std::list<int> data;
std::unordered_map<int, std::list<std::list<int>::iterator>> index;

public:
void enqueue(int x) {
auto it = data.insert(data.end(), x);
index[x].push_back(it);
}

int dequeue() {
if (data.empty()) throw std::out_of_range("Queue is empty");
int val = data.front();
auto it = index[val].front();
index[val].pop_front();
if (index[val].empty()) {
index.erase(val);
}
data.pop_front();
return val;
}

void remove(int x) {
if (index.count(x) == 0) return;
auto it = index[x].front();
data.erase(it);
index[x].pop_front();
if (index[x].empty()) {
index.erase(x);
}
}
};


▪️ Дополнительные вопросы на усложнение:


- Что если нужно сделать removeAll(x) — удаление всех вхождений элемента?
- Как изменится решение, если в очереди будут сложные объекты вместо int?
- Как минимизировать использование памяти, если очередь очень большая?
- Как обеспечить безопасность потоков (thread-safety) для многопоточного варианта очереди?

@cpluspluc

BY C++ Academy


Warning: Undefined variable $i in /var/www/tg-me/post.php on line 283

Share with your friend now:
tg-me.com/cpluspluc/1047

View MORE
Open in Telegram


C Academy Telegram | DID YOU KNOW?

Date: |

Can I mute a Telegram group?

In recent times, Telegram has gained a lot of popularity because of the controversy over WhatsApp’s new privacy policy. In January 2021, Telegram was the most downloaded app worldwide and crossed 500 million monthly active users. And with so many active users on the app, people might get messages in bulk from a group or a channel that can be a little irritating. So to get rid of the same, you can mute groups, chats, and channels on Telegram just like WhatsApp. You can mute notifications for one hour, eight hours, or two days, or you can disable notifications forever.

To pay the bills, Mr. Durov is issuing investors $1 billion to $1.5 billion of company debt, with the promise of discounted equity if the company eventually goes public, the people briefed on the plans said. He has also announced plans to start selling ads in public Telegram channels as soon as later this year, as well as offering other premium services for businesses and users.

C Academy from in


Telegram C++ Academy
FROM USA